/* global window */
import axios from 'axios'
import qs from 'qs'
import jsonp from 'jsonp'
import {ExceptionType} from 'enums'
import cloneDeep from 'lodash.clonedeep'
import pathToRegexp from 'path-to-regexp'
import {message} from 'antd'
import {CORS, YQL} from './config'
import {getToken} from './authority'

const codeMessage = {
  200: '服务器成功返回请求的数据。',
  201: '新建或修改数据成功。',
  202: '一个请求已经进入后台排队（异步任务）。',
  204: '删除数据成功。',
  400: '请求参数有误，请联系管理员。',
  401: '用户未认证，请重新登录。',
  403: '没有访问权限，请联系管理员。',
  404: '请求的页面不存在，请检查请求地址是否正确。',
  406: '请求的格式不可得。',
  410: '请求的资源被永久删除，且不会再得到的。',
  422: '当创建一个对象时，发生一个验证错误。',
  500: '服务器发生错误，请检查服务器。',
  502: '网关错误。',
  503: '服务不可用，服务器暂时过载或维护。',
  504: '网关超时。',
}

const fetch = (options) => {
  let {
    method = 'get',
    data,
    fetchType,
    url,
    header,
  } = options

  const cloneData = cloneDeep(data)

  try {
    let domain = ''
    if (url.match(/[a-zA-z]+:\/\/[^/]*/)) {
      [domain] = url.match(/[a-zA-z]+:\/\/[^/]*/)
      url = url.slice(domain.length)
    }
    const match = pathToRegexp.parse(url)
    url = pathToRegexp.compile(url)(data)
    for (let item of match) {
      if (item instanceof Object && item.name in cloneData) {
        delete cloneData[item.name]
      }
    }
    url = domain + url
  } catch (e) {
    message.error(e.message)
  }

  if (fetchType === 'JSONP') {
    return new Promise((resolve, reject) => {
      jsonp(url, {
        param: `${qs.stringify(data)}&callback`,
        name: `jsonp_${new Date().getTime()}`,
        timeout: 4000,
        header: header,
      }, (error, result) => {
        if (error) {
          reject(error)
        }
        resolve({statusText: 'OK', status: 200, data: result})
      })
    })
  } else if (fetchType === 'YQL') {
    url = `http://query.yahooapis.com/v1/public/yql?q=select * from json where url='${options.url}?${encodeURIComponent(qs.stringify(options.data))}'&format=json`
    data = null
  }

  switch (method.toLowerCase()) {
    case 'get':
      return axios.get(url, {
        headers: header,
        params: cloneData,
      })
    case 'delete':
      return axios.delete(url, {
        headers: header,
        data: cloneData,
      })
    case 'post':
      return axios.post(url, cloneData, {headers: header})
    case 'put':
      return axios.put(url, cloneData, {headers: header})
    case 'patch':
      return axios.patch(url, cloneData, {headers: header})
    default:
      return axios(options)
  }
}

export default function request (options) {
  const token = getToken()

  const tokenHeader = {
    'Authentication': token,
  }

  if (!(options.url.indexOf('login') >= 0 || options.url.indexOf('user') >= 0) && (!token || token === 'undefined')) {
    throw ExceptionType.NO_LOGIN
  }

  if (options.url && options.url.indexOf('//') > -1) {
    const origin = `${options.url.split('//')[0]}//${options.url.split('//')[1].split('/')[0]}`
    if (window.location.origin !== origin) {
      if (CORS && CORS.indexOf(origin) > -1) {
        options.fetchType = 'CORS'
      } else if (YQL && YQL.indexOf(origin) > -1) {
        options.fetchType = 'YQL'
      } else {
        options.fetchType = 'JSONP'
      }
    }
  }

  options.header = tokenHeader

  return fetch(options)
    .then((response) => {
      const {statusText, status} = response
      let data = options.fetchType === 'YQL' ? response.data.query.results.json : response.data
      if (data instanceof Array) {
        data = {
          list: data,
        }
      }
      return Promise.resolve({
        success: true,
        message: statusText,
        statusCode: status,
        ...data,
      })
    }).catch((error) => {
      const {response} = error
      let msg
      let statusCode
      const {data, statusText} = response
      if (response && response instanceof Object) {
        statusCode = response.status
        msg = data.msg
      } else {
        statusCode = 600
        msg = error.message || 'Network Error'
      }

      if (response.status >= 400 && response.status < 505)
        msg = msg ? msg : codeMessage[statusCode] || msg || statusText

      /* eslint-disable */
      return Promise.reject({code: response.status, statusCode, message: msg})
    })
}
